package pt.ist.meic.pava;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import jvstm.Transaction;
import jvstm.VBox;


public aspect JVSTMSync perthis(Pointcuts.shared(Object)){  
	
	private final ConcurrentMap<String, VBox<?>> boxes = new ConcurrentHashMap<String, VBox<?>>();
	
	public ConcurrentMap<String, VBox<?>> getBoxes() { 
		return this.boxes; }
	
	
	//TRANSACTIONS
	Object around() : Pointcuts.atomic() {
		//System.out.println("JVSTM - Atomic before");
		//System.out.println("JVSTM - Fields-" + obj.getClass().getDeclaredFields().length);
		Object ret = null;
		
		while (true){
			Transaction.begin();
			boolean finished = false;
			try{
				//System.out.println("JVSTM - Atomic before");
				ret = proceed();
				//System.out.println("JVSTM - Atomic after");
				Transaction.commit();
				finished = true;
				return ret;
			}
			catch(jvstm.CommitException ce){
				Transaction.abort();
				finished = true;
			}finally{
				if (!finished){
					Transaction.abort();
				}
			}
		}
	}
	
	//INIT
	before(Object obj) : Pointcuts.init(obj) {
		//System.out.println("JVSTM - Construtor after");
		//System.out.println("JVSTM - Fields-" + obj.getClass().getDeclaredFields().length);
		for(Field field : thisJoinPoint.getTarget().getClass().getDeclaredFields()){
			if(!Modifier.isStatic(field.getModifiers())){
				try {
					field.setAccessible(true);
					
					Object target = field.get(obj);
					
					String hash = obj.getClass().getCanonicalName() + field.getName();
					
					//System.out.println("JVSTM -  Adding to boxes the field " + field.getType().getName() + "\t\t" + field.getName());
					//System.out.println("JVSTM - adding " + hash + " " + field.get(obj));
					
					VBox<Object> box = new VBox<Object>(target);
					getBoxes().put(hash, box);
					
				} catch (IllegalArgumentException e) {
					System.err.println("JVSTM - Error on fields parsing!");
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					System.err.println("JVSTM - Error on fields parsing!");
					e.printStackTrace();
				}
			}
		}
	}
	
	//OBJECT
	Object around(Object obj) : Pointcuts.objectGetter(obj)&& 
	!Pointcuts.charGetter(Object) && 
	!Pointcuts.booleanGetter(Object) && 
	!Pointcuts.floatGetter(Object) && 
	!Pointcuts.longGetter(Object) && 
	!Pointcuts.shortGetter(Object) && 
	!Pointcuts.byteGetter(Object) && 
	!Pointcuts.doubleGetter(Object) && 
	!Pointcuts.intGetter(Object){
		
		String field = thisJoinPoint.getSignature().getName();
		
		//System.out.println("JVSTM - objectGetter MethodSignsture - " + field);
		thisJoinPoint.getSignature();
		String hash = obj.getClass().getCanonicalName() + field;
		//System.out.println("JVSTM - OBJ HASH - " + hash + "-signature-" + thisJoinPoint.getSignature());
		
		return getBoxes().get(hash).get();
	}
	
	void around(Object obj, Object i) : Pointcuts.objectSetter(obj, i) && 
	!Pointcuts.charSetter(Object, char) && 
	!Pointcuts.booleanSetter(Object, boolean) && 
	!Pointcuts.floatSetter(Object, float) && 
	!Pointcuts.longSetter(Object, long) && 
	!Pointcuts.shortSetter(Object, short) && 
	!Pointcuts.byteSetter(Object, byte) && 
	!Pointcuts.doubleSetter(Object, double) && 
	!Pointcuts.intSetter(Object, int){
		
		
		String field = thisJoinPoint.getSignature().getName();
		
		//		System.out.println("JVSTM - objectSetter MethodSignsture - " + field);
		thisJoinPoint.getSignature();
		String hash = obj.getClass().getCanonicalName() + field;
		
		getBoxes().put(hash, new VBox<Object>(i));
	}
	
	
	//CHAR
	char around(Object obj) : Pointcuts.charGetter(obj){
		
		String fieldName = thisJoinPoint.getSignature().getName();
		
		//System.out.println("JVSTM - charGetter MethodSignsture - " + fieldName);
		
		String hash = obj.getClass().getCanonicalName() + fieldName;
		
		Character by = (Character) getBoxes().get(hash).get();
		return by.charValue();
	}
	
	void around(Object obj, char i) : Pointcuts.charSetter(obj, i){
		String fieldName = thisJoinPoint.getSignature().getName();
		
		//System.out.println("JVSTM - charSetter MethodSignsture - " + fieldName);
		
		String hash = obj.getClass().getCanonicalName() + fieldName;
		
		@SuppressWarnings("unchecked")
		VBox<Character> o = (VBox<Character>) getBoxes().get(hash);
		o.put(new Character(i));
	}
	
	
	//BOOLEAN
	boolean around(Object obj) : Pointcuts.booleanGetter(obj){
		String fieldName = thisJoinPoint.getSignature().getName();
		
		//System.out.println("JVSTM - booleanGetter MethodSignsture - " + fieldName);
		
		String hash = obj.getClass().getCanonicalName()  + fieldName;
		
		Boolean by = (Boolean) getBoxes().get(hash).get();
		
		return by.booleanValue();
	}
	
	void around(Object obj, boolean i) : Pointcuts.booleanSetter(obj, i){
		String fieldName = thisJoinPoint.getSignature().getName();
		
		//System.out.println("JVSTM - booleanSetter MethodSignsture - " + fieldName);
		
		String hash = obj.getClass().getCanonicalName() +  fieldName;
		
		@SuppressWarnings("unchecked")
		VBox<Boolean> o = (VBox<Boolean>) getBoxes().get(hash);
		o.put(new Boolean(i));
	}
	
	//FLOAT
	float around(Object obj) : Pointcuts.floatGetter(obj){
		String fieldName = thisJoinPoint.getSignature().getName();
		
		//System.out.println("JVSTM - floatGetter MethodSignsture - " + fieldName);
		String hash = obj.getClass().getCanonicalName() + fieldName;
		
		Float by = (Float) getBoxes().get(hash).get();
		return by.floatValue();
	}
	
	void around(Object obj, float i) : Pointcuts.floatSetter(obj, i){
		String fieldName = thisJoinPoint.getSignature().getName();
		
		//System.out.println("JVSTM - floatSetter MethodSignsture - " + fieldName);
		
		String hash = obj.getClass().getCanonicalName() +  fieldName;
		
		@SuppressWarnings("unchecked")
		VBox<Float> o = (VBox<Float>) getBoxes().get(hash);
		o.put(new Float(i));
	}
	
	//LONG
	long around(Object obj) : Pointcuts.longGetter(obj){
		String fieldName = thisJoinPoint.getSignature().getName();
		
		//System.out.println("JVSTM - longGetter MethodSignsture - " + fieldName);
		
		String hash = obj.getClass().getCanonicalName() +  fieldName;
		
		Long by = (Long)  getBoxes().get(hash).get();
		
		return by.longValue();
	}
	
	void around(Object obj, long i) : Pointcuts.longSetter(obj, i){
		String fieldName = thisJoinPoint.getSignature().getName();
		
		//System.out.println("JVSTM - longSetter MethodSignsture - " + fieldName);
		
		String hash = obj.getClass().getCanonicalName() +  fieldName;
		
		@SuppressWarnings("unchecked")
		VBox<Long> o = (VBox<Long>) getBoxes().get(hash);
		o.put(new Long(i));
	}
	
	
	//SHORT
	short around(Object obj) : Pointcuts.shortGetter(obj){
		String fieldName = thisJoinPoint.getSignature().getName();
		
		//System.out.println("JVSTM - shortGetter MethodSignsture - " + fieldName);
		
		String hash = obj.getClass().getCanonicalName() + fieldName;
		
		Short by = (Short) getBoxes().get(hash).get();
		
		return by.shortValue();
	}
	
	void around(Object obj, short i) : Pointcuts.shortSetter(obj, i){
		String fieldName = thisJoinPoint.getSignature().getName();
		
		//System.out.println("JVSTM - shortSetter MethodSignsture - " + fieldName);
		
		String hash = obj.getClass().getCanonicalName() +  fieldName;
		
		@SuppressWarnings("unchecked")
		VBox<Short> o = (VBox<Short>) getBoxes().get(hash);
		o.put(new Short(i));
	}
	
	//BYTE
	byte around(Object obj) : Pointcuts.byteGetter(obj){
		String fieldName = thisJoinPoint.getSignature().getName();
		//System.out.println("JVSTM - byteGetter MethodSignsture - " + fieldName);
		
		String hash = obj.getClass().getCanonicalName() + fieldName;
		
		Byte by = (Byte) getBoxes().get(hash).get();
		
		return by.byteValue();
	}
	
	void around(Object obj, byte i) : Pointcuts.byteSetter(obj, i){
		String fieldName = thisJoinPoint.getSignature().getName();
		
		//System.out.println("JVSTM - byteSetter MethodSignsture - " + fieldName);
		
		String hash = obj.getClass().getCanonicalName() +  fieldName;
		
		@SuppressWarnings("unchecked")
		VBox<Byte> o = (VBox<Byte>) getBoxes().get(hash);
		o.put(new Byte(i));
	}
	
	//DOUBLE
	double around(Object obj) : Pointcuts.doubleGetter(obj){
		String fieldName = thisJoinPoint.getSignature().getName();
		
		//System.out.println("JVSTM - doubleGetter MethodSignsture - " + fieldName);
		
		String hash = obj.getClass().getCanonicalName() +  fieldName;
		
		Double doub = (Double) getBoxes().get(hash).get();
		
		return doub.doubleValue();
	}
	
	void around(Object obj, double i) : Pointcuts.doubleSetter(obj, i){
		String fieldName = thisJoinPoint.getSignature().getName();
		
		//System.out.println("JVSTM - doubleSetter MethodSignsture - " + fieldName);
		
		String hash = obj.getClass().getCanonicalName() +  fieldName;
		
		@SuppressWarnings("unchecked")
		VBox<Double> o = (VBox<Double>) getBoxes().get(hash);
		o.put(new Double(i));
	}
	
	//INT
	int around(Object obj) : Pointcuts.intGetter(obj){
		String fieldName = thisJoinPoint.getSignature().getName();
		
		//System.out.println("JVSTM - intGetter MethodSignsture - " + fieldName);
		
		String hash = obj.getClass().getCanonicalName() +  fieldName;
		
		Integer integer = (Integer) getBoxes().get(hash).get();
		
		return integer.intValue();
	}
	
	void around(Object obj, int i) : Pointcuts.intSetter(obj, i){
		String fieldName = thisJoinPoint.getSignature().getName();
		
		//System.out.println("JVSTM - intSetter MethodSignsture - " + fieldName);
		
		String hash = obj.getClass().getCanonicalName() + fieldName;
		
		@SuppressWarnings("unchecked")
		VBox<Integer> o = (VBox<Integer>) getBoxes().get(hash);
		o.put(new Integer(i));
	}
	
}
